/*******************************************************************************
 * Copyright IBM Corp. and others 2019
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution and
 * is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following
 * Secondary Licenses when the conditions for such availability set
 * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
 * General Public License, version 2 with the GNU Classpath
 * Exception [1] and GNU General Public License, version 2 with the
 * OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

/**
 * This script deletes old artifacts off the Artifactory server
 *
 * Parameters:
 *   JOB_TYPE: Choice - Keep artifacts based on TIME or COUNT
 *   JOB_TO_CHECK: String - The folder to keep x amount of builds in
 *   ARTIFACTORY_SERVER: String - The artifactory server to clean up
 *   ARTIFACTORY_REPO: String - The artifactory repo to clean up
 *   ARTIFACTORY_NUM_ARTIFACTS: String - How many artifacts to keep in artifactory
 *   ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS: String - Artifacts older than this will be deleted
 */

timestamps {
    def LABEL = params.LABEL ?: 'worker'
    node(LABEL) {
        checkout scm
        def variableFile = load 'buildenv/jenkins/common/variables-functions.groovy'
        variableFile.parse_variables_file()
        variableFile.set_basic_artifactory_config()
        println ARTIFACTORY_CONFIG

        def artifactory_server = params.ARTIFACTORY_SERVER ? params.ARTIFACTORY_SERVER : env.ARTIFACTORY_SERVER
        def server = Artifactory.server artifactory_server
        def ARTIFACTORY_SERVER_URL = server.getUrl()
        def ARTIFACTORY_REPO = params.ARTIFACTORY_REPO ? params.ARTIFACTORY_REPO : env.ARTIFACTORY_REPO
        def artifactoryCreds = server.getCredentialsId()
        def ARTIFACTORY_NUM_ARTIFACTS = params.ARTIFACTORY_NUM_ARTIFACTS ? params.ARTIFACTORY_NUM_ARTIFACTS : env.ARTIFACTORY_NUM_ARTIFACTS
        def ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS = params.ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS ? params.ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS : env.ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS
        ret = false
        try {
            retry(2) {
                if (ret) {
                    sleep time: 120, unit: 'SECONDS'
                } else {
                    ret = true
                }
                switch (params.JOB_TYPE) {
                    case 'TIME':
                        cleanupTime(ARTIFACTORY_SERVER_URL, ARTIFACTORY_REPO, ARTIFACTORY_DAYS_TO_KEEP_ARTIFACTS, artifactoryCreds)
                    break
                    case 'COUNT':
                        if (params.JOB_TO_CHECK) {
                            cleanupBuilds(ARTIFACTORY_SERVER_URL, ARTIFACTORY_REPO, params.JOB_TO_CHECK, ARTIFACTORY_NUM_ARTIFACTS, artifactoryCreds)
                        } else {
                            error 'Please input a job to cleanup'
                        }
                    break
                    default:
                        error 'the JOB_TO_CHECK parameter is not properly defined. Please Enter TIME or COUNT'
                    break
                }
            }
        } catch (e) {
            slackSend channel: '#jenkins-sandbox', color: 'danger', message: "Failed: ${JOB_NAME} #${BUILD_NUMBER} (<${BUILD_URL}|Open>)"
        }
    }
}

def cleanupBuilds(artifactory_server, artifactory_repo, jobToCheck, artifactory_num_artifacts, artifactoryCreds) {
    // This parameter is originally a string and needs to be casted as an Integer
    def artifactory_max_num_artifacts = artifactory_num_artifacts as Integer
    def testSubfolder = ''
    if (jobToCheck.contains("Test")) {
        testSubfolder = '/Test'
    }
    stage('Discover Stored Artifacts') {
        echo "Cleaning up ${jobToCheck}"
        echo "Keeping the latest ${artifactory_max_num_artifacts} builds"
        currentBuild.description = "Keeping ${artifactory_max_num_artifacts} builds of ${jobToCheck}"

        def request = httpRequest authentication: artifactoryCreds, consoleLogResponseBody: true, validResponseCodes: '200,404', url: "${artifactory_server}/api/storage/${artifactory_repo}${testSubfolder}/${jobToCheck}"

        requestStatus = request.getStatus()
        if (requestStatus == 200) {
            data = readJSON text: request.getContent()
            numberOfArtifacts = data.children.size()
            echo "There are ${numberOfArtifacts} builds"
        } else {
            currentBuild.description = "Warning: The URL does not exist<br>" + currentBuild.description
            echo 'Warning: This URL does not exist... EXITING'
            return
        }
    }
    if (requestStatus == 404) {
        return
    }
    stage('Delete Old Artifacts') {
        def amount_deleted = numberOfArtifacts - artifactory_max_num_artifacts
        if (amount_deleted > 0) {
            def folderNames = getFolderNumbers(data.children.uri)
            for (i = 0; i < amount_deleted; i++) {
                echo "Deleting Build #${folderNames[i]}"
                httpRequest authentication: artifactoryCreds, httpMode: 'DELETE', consoleLogResponseBody: true, url: "${artifactory_server}/${env.ARTIFACTORY_REPO}${testSubfolder}/${jobToCheck}/${folderNames[i]}"
            }
        } else {
            echo 'There are no artifacts to delete'
            amount_deleted = 0
        }
        if (artifactory_max_num_artifacts == 0) {
            echo "Deleting Entire Build '${jobToCheck}'"
            httpRequest authentication: artifactoryCreds, httpMode: 'DELETE', consoleLogResponseBody: true, url: "${artifactory_server}/${env.ARTIFACTORY_REPO}${testSubfolder}/${jobToCheck}"
        }
        currentBuild.description += "<br>Deleted ${amount_deleted} artifacts"
    }
}

def getFolderNumbers(folderURI) {
    def folderNumbers = []
    folderURI.each {
        folderNumbers.add(it.minus('/') as int)
    }
    return folderNumbers.sort()
}

def cleanupTime(artifactory_server, artifactory_repo , artifactory_days_to_keep_artifacts, artifactoryCreds) {
    stage('Discover Old Artifacts') {
        // This parameter is originally a string and needs to be casted as an Integer
        artifactory_days_to_keep_artifacts = artifactory_days_to_keep_artifacts as Integer
        def date = new Date()
        def current_time = date.getTime()
        def created_before_time = date.minus(artifactory_days_to_keep_artifacts).getTime()
        echo "Getting all artifacts over ${artifactory_days_to_keep_artifacts} days old"
        currentBuild.description = "Deleting build over ${artifactory_days_to_keep_artifacts} days"

        def request = httpRequest authentication: artifactoryCreds, consoleLogResponseBody: true, validResponseCodes: '200,404', url: "${artifactory_server}/api/search/usage?notUsedSince=${current_time}&createdBefore=${created_before_time}&repos=${artifactory_repo}"
        data = readJSON text: request.getContent()
        requestStatus = request.getStatus()
    }
    stage ('Delete Old Artifacts') {
        if (requestStatus == 200) {
            echo "There are ${data.results.size()} artifacts over ${artifactory_days_to_keep_artifacts} days old.\nCleaning them up"

            def artifacts_to_be_deleted = data.results.uri
            def artifactFolders = []
            artifacts_to_be_deleted.each() { uri ->
                artifactFolders.add(uri.substring(0,uri.lastIndexOf('/')).minus('/api/storage'))
            }
            artifactFoldersUnique = artifactFolders.unique()
            artifactFoldersUnique.each() { uri ->
                httpRequest authentication: artifactoryCreds, httpMode: 'DELETE', consoleLogResponseBody: true, url: uri
            }
            echo 'Deleted all the old artifacts'
            currentBuild.description += "<br>Deleted ${artifacts_to_be_deleted.size()} artifacts"
        } else if (requestStatus == 404) {
            currentBuild.description += "<br>Deleted 0 artifacts"
            if (data.errors.message.contains("No results found.")) {
                echo 'There are no artifacts to delete'
            } else {
                error 'HTTP 404 Not Found. Please check the logs'
            }
        } else {
            error 'Something went terribly wrong. Please check the logs'
        }
    }
}
